/*
 * Copyright 2025 LinQingYing. and contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * The use of this source code is governed by the Apache License 2.0,
 * which allows users to freely use, modify, and distribute the code,
 * provided they adhere to the terms of the license.
 *
 * The software is provided "as-is", and the authors are not responsible for
 * any damages or issues arising from its use.
 *
 */

package org.cangnova.cangjie.descriptors.impl

import org.cangnova.cangjie.descriptors.*
import org.cangnova.cangjie.descriptors.annotations.Annotations
import org.cangnova.cangjie.name.Name
import org.cangnova.cangjie.name.SpecialNames
import org.cangnova.cangjie.resolve.builtIns
import org.cangnova.cangjie.resolve.scopes.receivers.ContextReceiver
import org.cangnova.cangjie.resolve.scopes.receivers.ExtensionReceiver
import org.cangnova.cangjie.resolve.scopes.receivers.ImplicitContextReceiver
import org.cangnova.cangjie.types.*
import org.cangnova.cangjie.utils.SmartSet

open class PropertyDescriptorImpl(
    containingDeclaration: DeclarationDescriptor,
    original: PropertyDescriptor?,
    annotations: Annotations,
    override val modality: Modality,
    visibility: DescriptorVisibility,
    isVar: Boolean,
    name: Name,
    override val kind: CallableMemberDescriptor.Kind,
    source: SourceElement
) : VariableDescriptorWithInitializerImpl(containingDeclaration, annotations, name, null, isVar, source),
    PropertyDescriptor {

    override val original: PropertyDescriptor = original ?: this

    override var visibility: DescriptorVisibility = visibility


    private var _overriddenProperties: Collection<PropertyDescriptor>? = null
    private var _getter: PropertyGetterDescriptorImpl? = null
    private var _setter: PropertySetterDescriptor? = null
    private var _setterProjectedOut: Boolean = false

    override val returnType: CangJieType get() = type

    override val inType: CangJieType?
        get() = setter?.valueParameters?.get(0)?.type

    fun setInType(inType: CangJieType) {
        // Do nothing as the corresponding setter is generated by default
    }

    @Deprecated(
        message = "This method is left for binary compatibility with android.nav.safearg plugin. Used in SafeArgSyntheticDescriptorGenerator.kt"
    )
    fun setType(
        outType: CangJieType,
        typeParameters: List<TypeParameterDescriptor>,
        dispatchReceiverParameter: ReceiverParameterDescriptor?,
        extensionReceiverParameter: ReceiverParameterDescriptor?
    ) {
        setType(outType, typeParameters, dispatchReceiverParameter, extensionReceiverParameter, emptyList())
    }

    fun initialize(getter: PropertyGetterDescriptorImpl?, setter: PropertySetterDescriptor?) {
        this._getter = getter
        this._setter = setter
    }

    override val typeParameters: List<TypeParameterDescriptor>
        get() {
            val parameters = super.typeParameters
            // Diagnostics for EA-212070
            return parameters

        }

    override fun validate() {
        typeParameters
    }


    override val getter: PropertyGetterDescriptorImpl? get() = _getter

    override val setter: PropertySetterDescriptor? get() = _setter


    fun setSetterProjectedOut(setterProjectedOut: Boolean) {
        this._setterProjectedOut = setterProjectedOut
    }

    override val accessors: List<PropertyAccessorDescriptor>
        get() = buildList {
            getter?.let { add(it) }
            setter?.let { add(it) }
        }

    override fun substitute(substitutor: TypeSubstitutor): PropertyDescriptor {
        if (substitutor.isEmpty) return this

        return requireNotNull(
            newCopyBuilder()
                .setSubstitution(substitutor.substitution)
                .setOriginal(original)
                .build()
        )
    }

    override fun newCopyBuilder(): CallableMemberDescriptor.CopyBuilder<  PropertyDescriptor> = CopyConfiguration()

    private fun getSourceToUseForCopy(preserveSource: Boolean, original: PropertyDescriptor?): SourceElement =
        if (preserveSource) (original ?: this.original).source else SourceElement.NO_SOURCE

    /**
     * 创建并返回一个属性描述符的替代副本，基于给定的复制配置。
     *
     * @param copyConfiguration 包含复制属性所需的各种配置信息的对象。
     * @return 替代的属性描述符，如果无法创建则返回null。
     */
    protected fun doSubstitute(copyConfiguration: CopyConfiguration): PropertyDescriptor? {
        // 创建替代副本的基本属性
        val substitutedDescriptor = createSubstitutedCopy(
            copyConfiguration.owner,
            copyConfiguration.modality,
            copyConfiguration.visibility,
            copyConfiguration.original,
            copyConfiguration.kind,
            copyConfiguration.name,
            getSourceToUseForCopy(copyConfiguration.preserveSourceElement, copyConfiguration.original)
        )

        // 根据配置确定原始类型参数
        val originalTypeParameters = copyConfiguration.newTypeParameters ?: typeParameters

        // 初始化替代类型参数列表
        val substitutedTypeParameters = mutableListOf<TypeParameterDescriptor>()

        // 创建类型替换器并进行类型参数替换
        val substitutor = DescriptorSubstitutor.substituteTypeParameters(
            originalTypeParameters, copyConfiguration.substitution, substitutedDescriptor, substitutedTypeParameters
        )

        // 获取并替换输出类型
        val originalOutType = copyConfiguration.returnType
        val outType = substitutor.substitute(originalOutType, Variance.INVARIANT) ?: return null // TODO : 告知用户该属性已被投影出去

        // 替换分派接收器
        val substitutedDispatchReceiver = copyConfiguration.dispatchReceiverParameter?.let { dispatchReceiver ->
            dispatchReceiver.substitute(substitutor) ?: return null
        }

        // 替换扩展接收器
        val substitutedExtensionReceiver = extensionReceiverParameter?.let {
            substituteParameterDescriptor(substitutor, substitutedDescriptor, it)
        }

        // 替换上下文接收器
        val substitutedContextReceivers = contextReceiverParameters.mapNotNull { contextReceiverParameter ->
            substituteContextParameterDescriptor(substitutor, substitutedDescriptor, contextReceiverParameter)
        }

        // 设置替代描述符的类型信息
        substitutedDescriptor.setType(
            outType,
            substitutedTypeParameters,
            substitutedDispatchReceiver,
            substitutedExtensionReceiver,
            substitutedContextReceivers
        )

        // 创建并初始化替代的getter描述符
        val newGetter = getter?.let {
            PropertyGetterDescriptorImpl(
                substitutedDescriptor,
                it.annotations,
                copyConfiguration.modality,
                normalizeVisibility(it.visibility, copyConfiguration.kind),

                copyConfiguration.kind,
                copyConfiguration.originalGetter,
                SourceElement.NO_SOURCE
            ).apply {
                initialSignatureDescriptor =getSubstitutedInitialSignatureDescriptor(substitutor, it)

                initialize(it.returnType?.let { returnType -> substitutor.substitute(returnType, Variance.INVARIANT) })
            }
        }

        // 创建并初始化替代的setter描述符
        val newSetter = setter?.let {
            PropertySetterDescriptorImpl(
                substitutedDescriptor,
                it.annotations,
                copyConfiguration.modality,
                normalizeVisibility(it.visibility, copyConfiguration.kind),
                /*it.isDefault,*/
                copyConfiguration.kind,
                copyConfiguration.originalSetter,
                SourceElement.NO_SOURCE
            ).apply {
                var substitutedValueParameters = FunctionDescriptorImpl.getSubstitutedValueParameters(
                    this, it.valueParameters, substitutor, dropOriginal = false,
                    false, null
                )

                if (substitutedValueParameters == null) {
                    // 设置setter被投影出去，并创建一个虚拟的setter参数
                    substitutedDescriptor.setSetterProjectedOut(true)
                    substitutedValueParameters = listOf(
                        createSetterParameter(
                            this,
                            copyConfiguration.owner.builtIns.nothingType,
                            it.valueParameters[0].annotations
                        )
                    )
                }

                require(substitutedValueParameters.size == 1) { "Expected exactly one setter parameter" }
initialSignatureDescriptor = getSubstitutedInitialSignatureDescriptor(substitutor, it)

                initialize(substitutedValueParameters[0])
            }
        }

        // 初始化替代描述符的getter和setter
        substitutedDescriptor.initialize(newGetter, newSetter)

        // 如果配置要求，复制重写的描述符
        if (copyConfiguration.copyOverrides) {
            val overridden = SmartSet.create<CallableMemberDescriptor>().apply {
                overriddenDescriptors.forEach { propertyDescriptor ->
                    add(propertyDescriptor.substitute(substitutor))
                }
            }
            substitutedDescriptor.setOverriddenDescriptors(overridden)
        }

        return substitutedDescriptor
    }

    protected open fun createSubstitutedCopy(
        newOwner: DeclarationDescriptor,
        newModality: Modality,
        newVisibility: DescriptorVisibility,
        original: PropertyDescriptor?,
        kind: CallableMemberDescriptor.Kind,
        newName: Name,
        source: SourceElement
    ): PropertyDescriptorImpl = PropertyDescriptorImpl(
        newOwner, original, annotations, newModality, newVisibility, isVar, newName, kind, source
    )

    override fun <R, D> accept(visitor: DeclarationDescriptorVisitor<R, D>, data: D?): R? =
        visitor.visitPropertyDescriptor(this, data)

    override val overriddenDescriptors: Collection<PropertyDescriptor>
        get() = _overriddenProperties ?: emptyList()

    @Suppress("UNCHECKED_CAST")
    override fun setOverriddenDescriptors(overriddenDescriptors: Collection<CallableMemberDescriptor>) {
        this._overriddenProperties = overriddenDescriptors as Collection<PropertyDescriptor>
    }

    override fun copy(
        newOwner: DeclarationDescriptor,
        modality: Modality,
        visibility: DescriptorVisibility,
        kind: CallableMemberDescriptor.Kind,
        copyOverrides: Boolean
    ): PropertyDescriptor = newCopyBuilder()
        .setOwner(newOwner)
        .setOriginal(null)
        .setModality(modality)
        .setVisibility(visibility)
        .setKind(kind)
        .setCopyOverrides(copyOverrides)
        .build()!!

    override fun <V> getUserData(key: CallableDescriptor.UserDataKey<V>): V? = null

    inner class CopyConfiguration : CallableMemberDescriptor.CopyBuilder<PropertyDescriptor> {
        var owner: DeclarationDescriptor = containingDeclaration
            private set
        var modality: Modality = this@PropertyDescriptorImpl.modality
            private set
        var visibility: DescriptorVisibility = this@PropertyDescriptorImpl.visibility
            private set
        var original: PropertyDescriptor? = null
            private set
        var preserveSourceElement: Boolean = false
            private set
        var kind: CallableMemberDescriptor.Kind = this@PropertyDescriptorImpl.kind
            private set
        var substitution: TypeSubstitution = TypeSubstitution.EMPTY
            private set
        var copyOverrides: Boolean = true
            private set
        var dispatchReceiverParameter: ReceiverParameterDescriptor? =
            this@PropertyDescriptorImpl.dispatchReceiverParameter
            private set
        var newTypeParameters: List<TypeParameterDescriptor>? = null
            private set
        var name: Name = this@PropertyDescriptorImpl.name
            private set
        var returnType: CangJieType = this@PropertyDescriptorImpl.type
            private set

        override fun setOwner(owner: DeclarationDescriptor): CopyConfiguration = apply {
            this.owner = owner
        }

        override fun setOriginal(original: CallableMemberDescriptor?): CopyConfiguration = apply {
            this.original = original as PropertyDescriptor?
        }

        override fun setPreserveSourceElement(): CopyConfiguration = apply {
            preserveSourceElement = true
        }

        override fun setReturnType(type: CangJieType): CallableMemberDescriptor.CopyBuilder<PropertyDescriptor> = apply {
            returnType = type
        }

        override fun setModality(modality: Modality): CopyConfiguration = apply {
            this.modality = modality
        }

        override fun setVisibility(visibility: DescriptorVisibility): CopyConfiguration = apply {
            this.visibility = visibility
        }

        override fun setKind(kind: CallableMemberDescriptor.Kind): CopyConfiguration = apply {
            this.kind = kind
        }

        override fun setTypeParameters(parameters: List<TypeParameterDescriptor>): CallableMemberDescriptor.CopyBuilder<PropertyDescriptor> =
            apply {
                this.newTypeParameters = parameters
            }

        override fun setDispatchReceiverParameter(dispatchReceiverParameter: ReceiverParameterDescriptor?): CopyConfiguration =
            apply {
                this.dispatchReceiverParameter = dispatchReceiverParameter
            }

        override fun setSubstitution(substitution: TypeSubstitution): CopyConfiguration = apply {
            this.substitution = substitution
        }

        override fun setCopyOverrides(copyOverrides: Boolean): CopyConfiguration = apply {
            this.copyOverrides = copyOverrides
        }

        override fun setName(name: Name): CallableMemberDescriptor.CopyBuilder<PropertyDescriptor> = apply {
            this.name = name
        }

        override fun build(): PropertyDescriptor? = doSubstitute(this)

        val originalGetter: PropertyGetterDescriptor?
            get() = original?.getter

        val originalSetter: PropertySetterDescriptor?
            get() = original?.setter
    }

    companion object {
        fun create(
            containingDeclaration: DeclarationDescriptor,
            annotations: Annotations,
            modality: Modality,
            visibility: DescriptorVisibility,
            isVar: Boolean,
            name: Name,
            kind: CallableMemberDescriptor.Kind,
            source: SourceElement
        ): PropertyDescriptorImpl = PropertyDescriptorImpl(
            containingDeclaration, null, annotations, modality, visibility, isVar, name, kind, source
        )

        private fun normalizeVisibility(
            prev: DescriptorVisibility,
            kind: CallableMemberDescriptor.Kind
        ): DescriptorVisibility =
            if (kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE && DescriptorVisibilities.isPrivate(prev.normalize())) {
                DescriptorVisibilities.INVISIBLE_FAKE
            } else {
                prev
            }

        private fun substituteParameterDescriptor(
            substitutor: TypeSubstitutor,
            substitutedPropertyDescriptor: PropertyDescriptor,
            receiverParameterDescriptor: ReceiverParameterDescriptor
        ): ReceiverParameterDescriptor? {
            val substitutedType =
                substitutor.substitute(receiverParameterDescriptor.type, Variance.INVARIANT) ?: return null
            return ReceiverParameterDescriptorImpl(
                substitutedPropertyDescriptor,
                ExtensionReceiver(substitutedPropertyDescriptor, substitutedType, receiverParameterDescriptor.value),
                receiverParameterDescriptor.annotations
            )
        }

        private fun substituteContextParameterDescriptor(
            substitutor: TypeSubstitutor,
            substitutedPropertyDescriptor: PropertyDescriptor,
            receiverParameterDescriptor: ReceiverParameterDescriptor
        ): ReceiverParameterDescriptor? {
            val substitutedType =
                substitutor.substitute(receiverParameterDescriptor.type, Variance.INVARIANT) ?: return null
            return ReceiverParameterDescriptorImpl(
                substitutedPropertyDescriptor,
                ContextReceiver(
                    substitutedPropertyDescriptor,
                    substitutedType,
                    (receiverParameterDescriptor.value as ImplicitContextReceiver).customLabelName,
                    receiverParameterDescriptor.value
                ),
                receiverParameterDescriptor.annotations
            )
        }

        private fun getSubstitutedInitialSignatureDescriptor(
            substitutor: TypeSubstitutor,
            accessorDescriptor: PropertyAccessorDescriptor
        ): FunctionDescriptor? = accessorDescriptor.initialSignatureDescriptor?.substitute(substitutor)

        fun createSetterParameter(
            setterDescriptor: PropertySetterDescriptor,
            type: CangJieType,
            annotations: Annotations
        ): ValueParameterDescriptorImpl = ValueParameterDescriptorImpl(
            setterDescriptor,
            null,
            0,
            annotations,
            SpecialNames.IMPLICIT_SET_PARAMETER,
            false,
            type,
            isDefaultValue = false,
            source = SourceElement.NO_SOURCE
        )
    }
}